package com.qzsoft.zhjd.materiel.modules.materieldatainput.service.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.qzsoft.jeemis.common.exception.RenException;
import com.qzsoft.jeemis.common.validator.ValidatorUtils;
import com.qzsoft.jeemis.platform.security.user.SecurityUser;
import com.qzsoft.jeemis.platform.security.user.UserDetail;
import com.qzsoft.jeemis.repository.sys.entity.BasicCodeitemEntity;
import com.qzsoft.system.modules.sys.dict.service.SysDictService;
import com.qzsoft.zhjd.common.enums.BizCode;
import com.qzsoft.zhjd.common.enums.YesOrNo;
import com.qzsoft.zhjd.common.excel.ZhjdExcelExportUtils;
import com.qzsoft.zhjd.common.excel.style.ZhjdExcelExportTemplateStyler;
import com.qzsoft.zhjd.common.utils.FunctionUtils;
import com.qzsoft.zhjd.common.utils.RenExceptionUtils;
import com.qzsoft.zhjd.materiel.modules.materieldata.dto.MaterielDataDisableDto;
import com.qzsoft.zhjd.materiel.modules.materieldata.dto.MaterielDataListDto;
import com.qzsoft.zhjd.materiel.modules.materieldata.enmus.MaterielDataSourceEnum;
import com.qzsoft.zhjd.materiel.modules.materieldata.enmus.WeightTypeEnum;
import com.qzsoft.zhjd.materiel.modules.materieldata.entity.MaterielDataEntity;
import com.qzsoft.zhjd.materiel.modules.materieldata.service.MaterielDataService;
import com.qzsoft.zhjd.materiel.modules.materieldatainput.dto.MaterielDataInputDto;
import com.qzsoft.zhjd.materiel.modules.materieldatainput.service.MaterielDataInputService;
import com.qzsoft.zhjd.materiel.modules.materieldevice.entity.MaterielDeviceEntity;
import com.qzsoft.zhjd.materiel.modules.materieldevice.service.MaterielDeviceService;
import com.qzsoft.zhjd.materiel.modules.materielitem.entity.MaterielItemBaseEntity;
import com.qzsoft.zhjd.materiel.modules.materielitem.service.MaterielItemBaseService;
import com.qzsoft.zhjd.materiel.modules.materieloperator.entity.MaterielOperatorEntity;
import com.qzsoft.zhjd.materiel.modules.materieloperator.service.MaterielOperatorService;
import com.qzsoft.zhjd.materiel.modules.materielproject.entity.MaterielProjectEntity;
import com.qzsoft.zhjd.materiel.modules.materielproject.service.MaterielProjectService;
import com.qzsoft.zhjd.materiel.modules.materielsupplier.entity.MaterielSupplierEntity;
import com.qzsoft.zhjd.materiel.modules.materielsupplier.service.MaterielSupplierService;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName MaterielDataInputServiceImpl
 * @Description 磅单补录业务接口实现
 * @author linhuawei
 * @since 2022-04-05
 */
@Slf4j
@Service
public class MaterielDataInputServiceImpl implements MaterielDataInputService {

    @Autowired
    private MaterielDataService materielDataService;

    @Autowired
    private MaterielDeviceService materielDeviceService;

    @Autowired
    public SysDictService sysDictService;

    @Autowired
    private MaterielProjectService materielProjectService;

    @Autowired
    private MaterielSupplierService materielSupplierService;

    @Autowired
    private MaterielItemBaseService materielItemBaseService;
    
    @Autowired
    private MaterielOperatorService materielOperatorService;

    @Override
    public IPage<MaterielDataListDto> pageDto(Map<String, Object> params) {
        params.put("weightDataSource", MaterielDataSourceEnum.INPUT.getValue());
        return materielDataService.pageDto(params);
    }

    @Override
    public MaterielDataEntity getById(String id) {
        MaterielDataEntity entity = materielDataService.getById(id);
        if (MaterielDataSourceEnum.INPUT.getValue().equals(entity.getWeightDataSource())) {
            return entity;
        }
        return null;
    }

    @Override
    public void save(MaterielDataEntity entity) {
        // 参数校验
        check(entity);

        // 设置磅单编号
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String weightCode = entity.getDeviceCode() + sdf.format(entity.getEntryTime());
        QueryWrapper<MaterielDataEntity> queryWrapper = new QueryWrapper<MaterielDataEntity>();
        queryWrapper.eq("weight_code", weightCode);
        queryWrapper.eq("disable", YesOrNo.no.getValue());
        Integer count = materielDataService.count(queryWrapper);
        if (null != count && 0 < count) {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "磅单编号[" + weightCode + "]重复");
        }

        // 添加磅单
        entity.setWeightCode(weightCode);
        entity.setChanged(YesOrNo.yes.getValue());
        entity.setPrintTimes(0);
        materielDataService.save(entity);
    }

    @Override
    public void updateById(MaterielDataEntity entity) {
        // 判断磅单是否存在
        MaterielDataEntity temp = materielDataService.getById(entity.getId());
        if (null == temp || !MaterielDataSourceEnum.INPUT.getValue().equals(temp.getWeightDataSource())) {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "磅单数据不存在");
        }

        // 判断是否废弃磅单，废弃磅单不可修改
        if (YesOrNo.yes.getValue() == entity.getDisable()) {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "当前磅单已废弃，不可修改");
        }

        // 参数校验
        check(entity);

        // 处理磅单编号
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String weightCode = entity.getDeviceCode() + sdf.format(entity.getEntryTime());
        if (!weightCode.equals(temp.getWeightCode())) {
            QueryWrapper<MaterielDataEntity> queryWrapper = new QueryWrapper<MaterielDataEntity>();
            queryWrapper.eq("weight_code", weightCode);
            queryWrapper.eq("disable", YesOrNo.no.getValue());
            Integer count = materielDataService.count(queryWrapper);
            if (null != count && 0 < count) {
                RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "磅单编号[" + weightCode + "]重复");
            }
        }
        entity.setWeightCode(weightCode);

        // 修改磅单
        entity.setChanged(YesOrNo.yes.getValue());
        entity.setPrintTimes(temp.getPrintTimes());
        materielDataService.updateById(entity);
    }

    @Override
    public void disable(MaterielDataDisableDto dto) {
        // 判断磅单是否存在
        MaterielDataEntity temp = materielDataService.getById(dto.getId());
        if (null == temp || !MaterielDataSourceEnum.INPUT.getValue().equals(temp.getWeightDataSource())) {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "磅单数据不存在");
        }

        // 更新磅单状态
        UpdateWrapper<MaterielDataEntity> updateWrapper = new UpdateWrapper<MaterielDataEntity>();
        UserDetail user = SecurityUser.getUser();
        updateWrapper.eq("id", dto.getId());
        updateWrapper.set("disable", YesOrNo.yes.getValue());
        updateWrapper.set("disable_reason", dto.getDisableReason());
        updateWrapper.set("updater", user.getId());
        updateWrapper.set("update_date", new Date());
        updateWrapper.set("changed", YesOrNo.yes.getValue());
        materielDataService.update(updateWrapper);
    }

    @Override
    public void template(HttpServletResponse response) {
        // 获取数据字典
        List<BasicCodeitemEntity> weightTypeList = sysDictService.listByCodeid("WEIGHT_TYPE");
        List<BasicCodeitemEntity> reciveList = new ArrayList<BasicCodeitemEntity>();
        List<BasicCodeitemEntity> sendList = new ArrayList<BasicCodeitemEntity>();
        for (BasicCodeitemEntity item : weightTypeList) {
            if (WeightTypeEnum.RECEIVE.getValue().equals(item.getPptr())) {
                reciveList.add(item);
            } else if (WeightTypeEnum.SEND.getValue().equals(item.getPptr())) {
                sendList.add(item);
            }
        }

        List<MaterielDataInputDto> list = new ArrayList<MaterielDataInputDto>();
        String fileName = "磅单补录导入模板";
        ExportParams exportParams = new ExportParams(null, fileName);
        exportParams.setFixedTitle(false);
        exportParams.setStyle(ZhjdExcelExportTemplateStyler.class);

        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, MaterielDataInputDto.class, list);

        // 设置数据字典
        Sheet dicSheet = workbook.createSheet("dicsheet");
        workbook.setSheetHidden(1, true);
        int maxLen = reciveList.size();
        if (sendList.size() > maxLen) {
            maxLen = sendList.size();
        }
        maxLen = maxLen + 1;
        for (int i = 0; i < maxLen; i++) {
            dicSheet.createRow(i);
        }
        dicSheet.getRow(0).createCell(0).setCellValue(WeightTypeEnum.RECEIVE.getLabel());
        Name name = workbook.createName();
        name.setNameName(WeightTypeEnum.RECEIVE.getLabel());
        String formula = "dicsheet!$A$2:$A$" + (reciveList.size() + 1);
        name.setRefersToFormula(formula);
        for (int i = 0, len = reciveList.size(); i < len; i++) {
            dicSheet.getRow(i + 1).createCell(0).setCellValue(reciveList.get(i).getDescription());
        }
        dicSheet.getRow(0).createCell(1).setCellValue(WeightTypeEnum.SEND.getLabel());
        name = workbook.createName();
        name.setNameName(WeightTypeEnum.SEND.getLabel());
        formula = "dicsheet!$B$2:$B$" + (sendList.size() + 1);
        name.setRefersToFormula(formula);
        for (int i = 0, len = sendList.size(); i < len; i++) {
            dicSheet.getRow(i + 1).createCell(1).setCellValue(sendList.get(i).getDescription());
        }

        Sheet sheet = workbook.getSheetAt(0);
        int lastRow = 65535;
        CellRangeAddressList regions = new CellRangeAddressList(1, lastRow, 2, 2);
        DVConstraint constraint = DVConstraint.createFormulaListConstraint("dicsheet!$A$1:$B$1");
        HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint);
        sheet.addValidationData(dataValidation);

        regions = new CellRangeAddressList(1, lastRow, 3, 3);
        constraint = DVConstraint.createFormulaListConstraint("INDIRECT($B1)");
        dataValidation = new HSSFDataValidation(regions, constraint);
        sheet.addValidationData(dataValidation);

        String[] yeaOrNo = YesOrNo.nameArray();
        regions = new CellRangeAddressList(1, lastRow, 26, 26);
        constraint = DVConstraint.createExplicitListConstraint(yeaOrNo);
        dataValidation = new HSSFDataValidation(regions, constraint);
        sheet.addValidationData(dataValidation);

        try {
            ZhjdExcelExportUtils.exportExcel(response, fileName, workbook);
        } catch (Exception e) {
            log.error("磅单补录模板导出失败：{}", e);
            RenExceptionUtils.throwEx("数据导出失败");
        }
    }

    @Override
    public List<MaterielDataInputDto> importExcel(String deviceCode, MultipartFile file) {
        // 判断设备是否存在
        MaterielDeviceEntity device = materielDeviceService.getByCode(deviceCode);
        if (null == device) {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "地磅设备不存在");
        }

        // 解析excel文件
        List<MaterielDataInputDto> list = null;
        try {
            ImportParams params = new ImportParams();
            list = ExcelImportUtil.importExcel(file.getInputStream(), MaterielDataInputDto.class, params);
        } catch (Exception e) {
            log.error("磅单导入失败：{}", e);
            RenExceptionUtils.throwEx("磅单导入失败");
        }
        if (null == list || 0 == list.size()) {
            RenExceptionUtils.throwEx("磅单信息不存在");
        }

        // 获取数据字典
        List<BasicCodeitemEntity> weightTypeList = sysDictService.listByCodeid("WEIGHT_TYPE");
        Map<String, String> receiveMap = new HashMap<String, String>();
        Map<String, String> sendMap = new HashMap<String, String>();
        for (BasicCodeitemEntity item : weightTypeList) {
            if (WeightTypeEnum.RECEIVE.getValue().equals(item.getPptr())) {
                receiveMap.put(item.getDescription(), item.getCode());
            } else if (WeightTypeEnum.SEND.getValue().equals(item.getPptr())) {
                sendMap.put(item.getDescription(), item.getCode());
            }
        }

        // 获取供应商信息
        List<MaterielSupplierEntity> supplierList = materielSupplierService.getByBaseCode(device.getBaseCode());
        Map<String, String> supplierMap = new HashMap<String, String>();
        for (MaterielSupplierEntity supplier : supplierList) {
            supplierMap.put(supplier.getName(), supplier.getId());
        }

        // 获取项目信息
        List<MaterielProjectEntity> projectList = materielProjectService.getByBaseCode(device.getBaseCode());
        Map<String, String> projectMap = new HashMap<String, String>();
        for (MaterielProjectEntity project : projectList) {
            projectMap.put(project.getName(), project.getId());
        }

        // 获取材料信息
        List<MaterielItemBaseEntity> itemList = materielItemBaseService.getByBaseCode(device.getBaseCode());
        Set<String> itemSet = new HashSet<String>();
        for (MaterielItemBaseEntity item : itemList) {
            itemSet.add(item.getMaterielItemId());
        }
        
        // 获取操作员
        List<MaterielOperatorEntity> operatorList = materielOperatorService.getByBaseCode(device.getBaseCode());
        Map<String, String> operatorMap = new HashMap<String, String>();
        for (MaterielOperatorEntity operator : operatorList) {
            operatorMap.put(operator.getName(), operator.getId());
        }
        
        

        // 参数校验
        Set<String> weightCodeSet = new HashSet<String>();
        List<MaterielDataEntity> entityList = new ArrayList<MaterielDataEntity>();
        MaterielDataEntity entity;
        QueryWrapper<MaterielDataEntity> queryWrapper = new QueryWrapper<MaterielDataEntity>();
        Integer count;
        String materielItemId;
        for (MaterielDataInputDto dto : list) {
            // 参数校验
            try {
                ValidatorUtils.validateEntity(dto);
            } catch (RenException e) {
                dto.setSuccess(false);
                dto.setErrorMesg(e.getMessage());
                continue;
            }

            // 判断导入文件内部磅单编号是否重复
            if (weightCodeSet.contains(dto.getWeightCode())) {
                dto.setSuccess(false);
                dto.setErrorMesg("磅单编号重复");
                continue;
            } else {
                weightCodeSet.add(dto.getWeightCode());
            }

            // 判断磅单编号是否重复
            queryWrapper.clear();
            queryWrapper.eq("weight_code", dto.getWeightCode());
            queryWrapper.eq("disable", YesOrNo.no.getValue());
            count = materielDataService.count(queryWrapper);
            if (null != count && 0 < count) {
                dto.setSuccess(false);
                dto.setErrorMesg("磅单编号重复");
                continue;
            }

            // 判断收发料类型，根据收发料类型判断供应商、用料项目/单位是否为空
            if (WeightTypeEnum.RECEIVE.getLabel().equals(dto.getWeightTypeLabel())) {
                // 判断收发料类型
                if(null == receiveMap.get(dto.getWeightDataTypeLabel())) {
                    dto.setSuccess(false);
                    dto.setErrorMesg("未知的收发料类型");
                    continue;
                }
                
                // 判断供应商信息
                if (ObjectUtils.isEmpty(dto.getSupplierName()) || null == supplierMap.get(dto.getSupplierName())) {
                    dto.setSuccess(false);
                    dto.setErrorMesg("供应商不可为空");
                    continue;
                }

                // 填充参数
                dto.setWeightType(WeightTypeEnum.RECEIVE.getValue());
                dto.setWeightDataType(receiveMap.get(dto.getWeightDataTypeLabel()));
                dto.setSupplierId(supplierMap.get(dto.getSupplierName()));
                dto.setProjectId(null);// 收料不需要用料项目/单位
            } else if (WeightTypeEnum.SEND.getLabel().equals(dto.getWeightTypeLabel())) {
                // 判断收发料类型
                if(null == sendMap.get(dto.getWeightDataTypeLabel())) {
                    dto.setSuccess(false);
                    dto.setErrorMesg("未知的收发料类型");
                    continue;
                }
                
                // 判断项目信息
                if (ObjectUtils.isEmpty(dto.getProjectName()) || null == projectMap.get(dto.getProjectName())) {
                    dto.setSuccess(false);
                    dto.setErrorMesg("用料项目/单位不可为空");
                    continue;
                }

                // 填充参数
                dto.setWeightType(WeightTypeEnum.SEND.getValue());
                dto.setWeightDataType(sendMap.get(dto.getWeightDataTypeLabel()));
                dto.setProjectId(projectMap.get(dto.getProjectName()));
                dto.setSupplierId(null);// 发料不需要供应商
            } else {
                dto.setSuccess(false);
                dto.setErrorMesg("未知的磅单类型");
                continue;
            }
            
            // 判断材料信息
            materielItemId = dto.getMaterielItemName();
            if(ObjectUtils.isNotEmpty(dto.getMaterielItemSpec())) {
                materielItemId = materielItemId + dto.getMaterielItemSpec();
            }
            materielItemId = FunctionUtils.md5L32(materielItemId);
            if(!itemSet.contains(materielItemId)) {
                dto.setSuccess(false);
                dto.setErrorMesg("材料不存在");
                continue;
            }
            dto.setMaterielItemId(materielItemId);
            
            // 判断是否废弃磅单
            if(null != dto.getDisableLabel() && YesOrNo.yes.getName().equals(dto.getDisableLabel())) {
                if(ObjectUtils.isEmpty(dto.getDisableReason())) {
                    dto.setSuccess(false);
                    dto.setErrorMesg("废弃原因不存在");
                    continue;
                }
                
                dto.setDisable(YesOrNo.yes.getValue());
            } else {
                dto.setDisable(YesOrNo.no.getValue());
                dto.setDisableReason(null);
            }
            
            // 判断操作员
            if(null == operatorMap.get(dto.getOperatorName())) {
                dto.setSuccess(false);
                dto.setErrorMesg("操作员不存在");
                continue;
            } else {
                dto.setOperatorId(operatorMap.get(dto.getOperatorName()));
            }

            // 设置需要保存的数据
            dto.setSuccess(true);
            entity = new MaterielDataEntity();
            BeanUtils.copyProperties(dto, entity);
            entity.setBaseCode(device.getBaseCode());
            entity.setDeviceCode(deviceCode);
            entity.setWeightDataSource(MaterielDataSourceEnum.INPUT.getValue());
            entityList.add(entity);
        }
        materielDataService.saveBatch(entityList, 100);

        return list;
    }

    @Override
    public String getLastYear(Map<String, Object> params) {
        params.put("weightDataSource", MaterielDataSourceEnum.INPUT.getValue());
        return materielDataService.getLastYear(params);
    }

    private void check(MaterielDataEntity entity) {
        // 判断收发料类型，根据收发料类型判断供应商、用料项目/单位是否为空
        if (WeightTypeEnum.RECEIVE.getValue().equals(entity.getWeightType())) {
            if (ObjectUtils.isEmpty(entity.getSupplierId())) {
                RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "供应商不可为空");
            }
            entity.setProjectId(null);// 收料不需要用料项目/单位
        } else if (WeightTypeEnum.SEND.getValue().equals(entity.getWeightType())) {
            if (ObjectUtils.isEmpty(entity.getProjectId())) {
                RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "用料项目/单位不可为空");
            }
            entity.setSupplierId(null);// 发料不需要供应商
        } else {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "未知的收发料类型");
        }

        // 判断地磅是否存在
        MaterielDeviceEntity device = materielDeviceService.getByCode(entity.getDeviceCode());
        if (null == device) {
            RenExceptionUtils.throwEx(BizCode.PARAMS_ERROR, "地磅设备不存在");
        }

        // 设置基本信息
        entity.setBaseCode(device.getBaseCode());// 基地信息
        entity.setDeviceCode(device.getCode());// 设备编号
        entity.setWeightDataSource(MaterielDataSourceEnum.INPUT.getValue());// 数据来源为补录
        entity.setDisable(YesOrNo.no.getValue()); // 新添加磅单不可能是废弃状态
        entity.setDisableReason(null);
    }

}
